home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 10 - 1994 / 10.09 Sep 94 / Programmers' Challenge / RGBtoYUVInit.c < prev   
Encoding:
C/C++ Source or Header  |  1994-07-09  |  6.5 KB  |  251 lines  |  [TEXT/KAHL]

  1. /*
  2.     RGBtoYUVInit.c - Sets up the tables for use by RGBtoYUV.
  3.     
  4.     MacTech Magazine Programmers' Challenge
  5.     July, 1994
  6.     Written by Robert A. Noll
  7.     
  8.     Copyright (c) 1994 Robert A. Noll
  9. */
  10.  
  11. #include "RGBtoYUV.h"
  12.  
  13. /*
  14.     BigNum - A fixed point number type with 8 bits of 
  15.     intregral part and 88 bits of fractional part.
  16. */
  17. typedef struct {
  18.     unsigned long val[3];
  19.     } BigNum;
  20.     
  21. typedef struct {
  22.     BigNum            y[256];
  23.     BigNum            g[256];
  24.     BigNum            b[256];
  25.     } TempBlock;
  26.  
  27. /*
  28.     Multiply - Takes a BigNum and multiplies it by a
  29.     number in the range 0 - 255.  Result is put into 'ans'.
  30. */
  31. void Multiply(void* bits, unsigned char num, void* ans)
  32.     {
  33.     int x;
  34.     unsigned short* bp = (unsigned short*)bits + 5;
  35.     unsigned short* ap = (unsigned short*)ans + 5;
  36.     unsigned long val = 0;
  37.     
  38.     for (x=6; x; --x,--bp,--ap) {
  39.         val += (unsigned long)*bp * num;
  40.         *ap = val;
  41.         val >>= 16;
  42.         }
  43.     }
  44.  
  45. /*
  46.     Add - Adds two BigNums and returns the result in 'ans'
  47. */
  48. void Add(void* bits1, void* bits2, void* ans)
  49.     {
  50.     int x;
  51.     unsigned short* bp1 = (unsigned short*)bits1 + 5;
  52.     unsigned short* bp2 = (unsigned short*)bits2 + 5;
  53.     unsigned short* ap = (unsigned short*)ans + 5;
  54.     unsigned long val = 0;
  55.     
  56.     for (x=6; x; --x,--bp1,--bp2,--ap) {
  57.         val += (unsigned long)*bp1 + *bp2;
  58.         *ap = val;
  59.         val >>= 16;
  60.         }
  61.     }
  62.     
  63. /*
  64.     RGBtoYUVInit - Sets up all the tables used by the
  65.     'RGBtoYUV' function.
  66.     
  67.     •    Since accuracy is a concern I use my own number
  68.         format and routines to calculate tables.  It turns
  69.         out that FP routines are just as accurate but my
  70.         stuff is 3x faster.
  71.         
  72.     About calculations in general:
  73.     
  74.     •    A table with all 16M values for each of Y, U, and
  75.         V would be nice but, at 48MB, would be way over
  76.         1MB limit.  With minor calcs in 'RGBtoYUV' I need
  77.         two 64K entry tables for each of U and V.  At full
  78.         precision they would be 768K each.  They get
  79.         reduced greatly later.  The Y table I break into
  80.         two tables: one for the R calcs and one for the
  81.         G and B combination.  At full precision they would
  82.         take 3K and 768K respectivly.  They get reduced
  83.         later also.  At this point we are a lot closer to
  84.         the 1MB limit.
  85.         
  86.     About Y calculations:
  87.     
  88.     •    24 bits of fractional precision for the two tables
  89.         was found to give proper results.  This was found
  90.         by checking the result of rounding the full
  91.         precision result against the rounding of the sum
  92.         of the two table entries for EVERY one of the 16M
  93.         combinations.  This allows us to represent our
  94.         table entries in tables of 'unsigned long's.  Table
  95.         sizes are thus reduced to 1K and 256K.
  96.     •    Rounding is handled by adding
  97.         .7FFFFFFFFFFFFFFFFFFFFF to the 'gb' table values.
  98.         This allows RGBtoYUV to just truncate its result.
  99.     •    When R, G, and B are all equal then Y is the same
  100.         number.  With the tables that are built the
  101.         checking for such a limited case (256 out of
  102.         16,777,216) just added space.  (To calc 16M values
  103.         takes a little over 2 minutes)
  104.         
  105.     About U and V in general:
  106.     
  107.     •    Since B and R are multipied by .5 for these calcs
  108.         we can except them as 7 bits of integral part and
  109.         1 bit of fraction.
  110.     •    Since the B or R calculation will always result in
  111.         some number X.0 or X.5.  This enables us to do all
  112.         rounding in the table values.  Also we are able to
  113.         store the table numbers in the same 7.1 bit format.
  114.     •    If rounding is always down then RGBtoYUV can just
  115.         truncate after the subtraction.  If rounding is
  116.         to be toward zero then special handling is needed.
  117.         
  118.     About V calculations:
  119.     
  120.     •    Because of the number of decimal places and the
  121.         fact that the multipliers add up to .50000001 the
  122.         G and B combination will never result in exactly
  123.         X.0 or X.5.  Thus for V we don't need to worry
  124.         about whether -0.5 rounds up or down.
  125.         
  126.     About U calculations:
  127.     
  128.     •    The R and G combination values will result in an
  129.         X.0 or X.5 result when R and G are equal.
  130.     •    If rounding is always down (.5 -> 0 and -.5 -> -1)
  131.         then truncation after the calculation is all that
  132.         is needed.
  133.     •    If rounding is always towards zero (.5 -> 0 and
  134.         -.5 -> 0) then RGBtoYUV will add .5 and then
  135.         truncate when R and G are equal.  All other cases
  136.         will still be handled with just truncation.
  137. */    
  138. void *RGBtoYUVInit(void)
  139.     {
  140.     short r,g,b;
  141.  
  142.     BigNum mat[] = {
  143.        { 0x004C8B43, 0x95810624, 0xDD2F1A9F },/*.29900000*/
  144.        { 0x009645A1, 0xCAC08312, 0x6E978D4F },/*.58700000*/
  145.        { 0x001D2F1A, 0x9FBE76C8, 0xB4395810 },/*.11400000*/
  146.        { 0x002B3246, 0xA4293F94, 0x6A85AFD7 },/*.16873590*/
  147.        { 0x0054CDB9, 0x5BD6C06B, 0x957A5028 },/*.33126410*/
  148.        { 0x00800000, 0x00000000, 0x00000000 },/*.50000000*/
  149.        { 0x00800000, 0x00000000, 0x00000000 },/*.50000000*/
  150.        { 0x006B2F1C, 0x4D3DA074, 0x7F2DDD88 },/*.41868760*/
  151.        { 0x0014D0E3, 0xDDB57D4F, 0xE1EA9636 },/*.08131241*/
  152.        { 0x007FFFFF, 0xFFFFFFFF, 0xFFFFFFFF } /*.49999999*/
  153.        };
  154.     unsigned long *rp, *gbp;
  155.     BigNum *gp, *bp, *yp;
  156.     BigNum yt, ut, vt, xt;
  157.     unsigned char *up, *vp;
  158.     PrivateBlock *pb;
  159.     TempBlock *tb;
  160.  
  161.     pb = (PrivateBlock*)NewPtr(sizeof(PrivateBlock));
  162.     if (pb==0) {
  163.         DebugStr("\pOut of Memory");
  164.         return;
  165.         }
  166.     pb->rp = pb->r;
  167.     pb->gbp = pb->gb;
  168.     pb->up = pb->u;
  169.     pb->vp = pb->v;
  170.     
  171.     tb = (TempBlock*)NewPtr(sizeof(TempBlock));
  172.     if (tb==0) {
  173.         DebugStr("\pOut of Memory");
  174.         return;
  175.         }
  176.     
  177. /*
  178.     r-values never = x.0 or x.5
  179. */
  180.     rp = pb->rp;
  181.     for (r=0; r<256; ++r,++rp) {
  182.         Multiply(&mat[0],r,&xt);
  183.         *rp = *((unsigned long*)&xt);
  184.         }
  185.     
  186. /*
  187.     This method same as 0-255, 0-255 method.
  188.     
  189.     My calc routines equal SANE routines and are 3 times
  190.      faster.
  191.      
  192.     u-values are never == x.0 or x.5
  193.     v-values are never == x.0 or x.5
  194. */
  195.      yp = tb->y;
  196.      gp = tb->g;
  197.      bp = tb->b;
  198.     gbp = pb->gbp;
  199.      up = pb->up;
  200.      vp = pb->vp;
  201.     for (b=0; b<256; ++b,++yp,++gp,++bp,++gbp,++up,++vp) {
  202.         Multiply(&mat[2],b,yp);
  203.         Add(yp,&mat[9],yp);
  204.         *gbp = *((unsigned long*)yp);
  205.         Multiply(&mat[4],b,gp);
  206.         /*Add(gp,&mat[9],gp);*/
  207.         *up = *((unsigned short*)gp) >> 7;
  208.         Multiply(&mat[8],b,bp);
  209.         /*Add(bp,&mat[9],bp);*/
  210.         *vp = *((unsigned short*)bp) >> 7;
  211.         }
  212.     for (g=1; g<256; ++g) {
  213.         Multiply(&mat[1],g,&yt);
  214.         Add(&yt,&mat[9],&xt);
  215.         *gbp = *((unsigned long*)&xt);
  216.         ++gbp;
  217.         Multiply(&mat[3],g,&ut);
  218.         /*Add(&ut,&mat[9],&xt);*/
  219.         *up = *((unsigned short*)&ut) >> 7;
  220.         ++up;
  221.         Multiply(&mat[7],g,&vt);
  222.         /*Add(&vt,&mat[9],&xt);*/
  223.         *vp = *((unsigned short*)&vt) >> 7;
  224.         ++vp;
  225.         yp = &tb->y[1];
  226.         gp = &tb->g[1];
  227.         bp = &tb->b[1];
  228.         for (b=1; b<256;
  229.                 ++b,++gbp,++yp,++up,++gp,++vp,++bp) {
  230.             Add(&yt,yp,&xt);
  231.             *gbp = *((unsigned long*)&xt);
  232.             if (g==b) {
  233.                 *up = b;
  234.                 *vp = b;
  235.                 }
  236.             else {
  237.                 Add(&ut,gp,&xt);
  238.                 *up = *((unsigned short*)&xt) >> 7;
  239.                 Add(&vt,bp,&xt);
  240.                 *vp = *((unsigned short*)&xt) >> 7;
  241.                 }
  242.             }
  243.         }
  244.     
  245.     if (tb!=0)
  246.         DisposPtr((Ptr)tb);
  247.  
  248.     pb->sig = 'RNCS';
  249.     return pb;
  250.     }
  251.